#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
# @Date : 2024/4/10 15:40
# @Author : water
# @Description : 模块

"""
模块
    在前面的几个章节中我们基本上是用 python 解释器来编程，如果你从 Python 解释器退出再进入，那么你定义的所有的方法和变量就都消失了。
    为此 Python 提供了一个办法，把这些定义存放在文件中，为一些脚本或者交互式的解释器实例使用，这个文件被称为模块。
    模块是一个包含所有你定义的函数和变量的文件，其后缀名是.py。模块可以被别的程序引入，以使用该模块中的函数等功能。这也是使用 python 标准库的方法。
import 语句
    想使用 Python 源文件，只需在另一个源文件里执行 import 语句，语法如下：

    import module1[, module2[,... moduleN]
    当解释器遇到 import 语句，如果模块在当前的搜索路径就会被导入。

    搜索路径时一个解释器会先进行搜索的所有目录的列表。如想要导入模块 support，需要把命令放在脚本的顶端：
    注意：绝对和相对搜索路径问题
from … import 语句
    Python 的 from 语句让你从模块中导入一个指定的部分到当前命名空间中，语法如下：
        from modname import name1[, name2[, ... nameN]]
from … import * 语句
    把一个模块的所有内容全都导入到当前的命名空间也是可行的，只需使用如下声明：
        from modname import *
    这提供了一个简单的方法来导入一个模块中的所有项目。然而这种声明不该被过多地使用
深入模块
    模块除了方法定义，还可以包括可执行的代码。这些代码一般用来初始化这个模块。这些代码只有在第一次被导入时才会被执行。
    每个模块有各自独立的符号表，在模块内部为所有的函数当作全局符号表来使用。
    所以，模块的作者可以放心大胆的在模块内部使用这些全局变量，而不用担心把其他用户的全局变量搞混。
    从另一个方面，当你确实知道你在做什么的话，你也可以通过 modname.itemname 这样的表示法来访问模块内的函数。
    模块是可以导入其他模块的。在一个模块（或者脚本，或者其他地方）的最前面使用 import 来导入一个模块，当然这只是一个惯例，而不是强制的。被导入的模块的名称将被放入当前操作的模块的符号表中。
    还有一种导入的方法，可以使用 import 直接把模块内（函数，变量的）名称导入到当前操作模块

    这将把所有的名字都导入进来，但是那些由单一下划线（_）开头的名字不在此例。大多数情况， Python程序员不使用这种方法，因为引入的其它来源的命名，很可能覆盖了已有的定义。
__name__属性
    一个模块被另一个程序第一次引入时，其主程序将运行。如果我们想在模块被引入时，模块中的某一程序块不执行，我们可以用__name__属性来使该程序块仅在该模块自身运行时执行。
dir() 函数
    内置的函数 dir() 可以找到模块内定义的所有名称
标准模块
    Python 本身带着一些标准的模块库。
    有些模块直接被构建在解析器里，这些虽然不是一些语言内置的功能，但是他却能很高效的使用，甚至是系统级调用也没问题。
    这些组件会根据不同的操作系统进行不同形式的配置，比如 winreg 这个模块就只会提供给 Windows 系统。
    应该注意到这有一个特别的模块 sys ，它内置在每一个 Python 解析器中。
包
    包是一种管理 Python 模块命名空间的形式，采用"点模块名称"。
    比如一个模块的名称是 A.B， 那么他表示一个包 A中的子模块 B 。
    就好像使用模块的时候，你不用担心不同模块之间的全局变量相互影响一样，采用点模块名称这种形式也不用担心不同库之间的模块重名的情况。
    这样不同的作者都可以提供 NumPy 模块，或者是 Python 图形库。
    不妨假设你想设计一套统一处理声音文件和数据的模块（或者称之为一个"包"）。
    现存很多种不同的音频文件格式（基本上都是通过后缀名区分的，例如： .wav，:file:.aiff，:file:.au，），所以你需要有一组不断增加的模块，用来在不同的格式之间转换。
    并且针对这些音频数据，还有很多不同的操作（比如混音，添加回声，增加均衡器功能，创建人造立体声效果），所以你还需要一组怎么也写不完的模块来处理这些操作。
    这里给出了一种可能的包结构（在分层的文件系统中）:
        sound/                          顶层包
          __init__.py               初始化 sound 包
          formats/                  文件格式转换子包
                  __init__.py
                  wavread.py
                  wavwrite.py
                  aiffread.py
                  aiffwrite.py
                  auread.py
                  auwrite.py
                  ...
          effects/                  声音效果子包
                  __init__.py
                  echo.py
                  surround.py
                  reverse.py
                  ...
          filters/                  filters 子包
                  __init__.py
                  equalizer.py
                  vocoder.py
                  karaoke.py
                  ...
    在导入一个包的时候，Python 会根据 sys.path 中的目录来寻找这个包中包含的子目录。
    目录只有包含一个叫做 __init__.py 的文件才会被认作是一个包，主要是为了避免一些滥俗的名字（比如叫做 string）不小心的影响搜索路径中的有效模块。
    最简单的情况，放一个空的 :file:__init__.py就可以了。当然这个文件中也可以包含一些初始化代码或者为（将在后面介绍的） __all__变量赋值。
从一个包中导入*
    如果我们使用 from sound.effects import * 会发生什么呢？
    Python 会进入文件系统，找到这个包里面所有的子模块，然后一个一个的把它们都导入进来。
    但这个方法在 Windows 平台上工作的就不是非常好，因为 Windows 是一个不区分大小写的系统。
    在 Windows 平台上，我们无法确定一个叫做 ECHO.py 的文件导入为模块是 echo 还是 Echo，或者是 ECHO。
    为了解决这个问题，我们只需要提供一个精确包的索引。
    导入语句遵循如下规则：如果包定义文件 __init__.py 存在一个叫做 __all__ 的列表变量，那么在使用 from package import * 的时候就把这个列表中的所有名字作为包内容导入。
    作为包的作者，可别忘了在更新包之后保证 __all__ 也更新了啊。
    注意：
        通常我们并不主张使用 * 这种方法来导入模块，因为这种方法经常会导致代码的可读性降低。不过这样倒的确是可以省去不少敲键的功夫，而且一些模块都设计成了只能通过特定的方法导入。
        使用 from Package import specific_submodule 这种方法永远不会有错。事实上，这也是推荐的方法。除非是你要导入的子模块有可能和其他包的子模块重名。
"""

import sys


def print_sys():
    """
    1、import sys 引入 python 标准库中的 sys.py 模块；这是引入某一模块的方法。
    2、sys.argv 是一个包含命令行参数的列表。
    3、sys.path 包含了一个 Python 解释器自动查找所需模块的路径的列表。
    """
    print('命令行参数如下:')
    for i in sys.argv:
        print(i)

    print('\n\nPython 路径为：', sys.path, '\n')


def print_func(par):
    print("Hello : ", par)
    return


# 斐波那契(fibonacci)数列模块

def fib(n):  # 定义到 n 的斐波那契数列
    a, b = 0, 1
    while b < n:
        print(b, end=' ')
        a, b = b, a + b
    print()


def fib2(n):  # 返回到 n 的斐波那契数列
    result = []
    a, b = 0, 1
    while b < n:
        result.append(b)
        a, b = b, a + b
    return result


# __name__属性
"""
说明： 每个模块都有一个__name__属性，当其值是'__main__'时，表明该模块自身在运行，否则是被引入。
说明：__name__ 与 __main__ 底下是双下划线， _ _ 是这样去掉中间的那个空格。
"""
if __name__ == '__main__':
    print('这个程序被当做主程序运行')
    dir(sys)
else:
    print('这个程序被当做模块运行')

# 变量 sys.ps1 和 sys.ps2 定义了主提示符和副提示符所对应的字符串:
sys.ps1 = "hello sys"
print(sys.ps1)
sys.ps2 = "hello world"
print(sys.ps2)
